// PART OF THE MACHINE SIMULATION. DO NOT CHANGE.

package nachos.machine;

import java.awt.Frame;
import java.awt.Dimension;
import java.awt.Panel;
import java.awt.Canvas;

import java.awt.GridLayout;
import java.awt.Rectangle;
import java.awt.Graphics;
import java.awt.Color;

/**
 * A graphical visualization for the <tt>ElevatorBank</tt> class.
 */
public final class ElevatorGui extends Frame {
	private final static int w = 90, h = 75;

	private int numFloors, numElevators;

	private ElevatorShaft[] elevators;
	private Floor[] floors;

	private int totalWidth, totalHeight;

	ElevatorGui(int numFloors, int numElevators, int[] numRidersPerFloor) {
		this.numFloors = numFloors;
		this.numElevators = numElevators;

		totalWidth = w * (numElevators + 1);
		totalHeight = h * numFloors;

		setTitle("Elevator Bank");

		Panel floorPanel = new Panel(new GridLayout(numFloors, 1, 0, 0));

		floors = new Floor[numFloors];
		for (int i = numFloors - 1; i >= 0; i--) {
			floors[i] = new Floor(i, numRidersPerFloor[i]);
			floorPanel.add(floors[i]);
		}

		Panel panel = new Panel(new GridLayout(1, numElevators + 1, 0, 0));

		panel.add(floorPanel);

		elevators = new ElevatorShaft[numElevators];
		for (int i = 0; i < numElevators; i++) {
			elevators[i] = new ElevatorShaft(i);
			panel.add(elevators[i]);
		}

		add(panel);
		pack();

		setVisible(true);

		repaint();
	}

	void openDoors(int elevator) {
		elevators[elevator].openDoors();
	}

	void closeDoors(int elevator) {
		elevators[elevator].closeDoors();
	}

	void setDirectionDisplay(int elevator, int direction) {
		elevators[elevator].setDirectionDisplay(direction);
	}

	void pressUpButton(int floor) {
		floors[floor].pressUpButton();
	}

	void clearUpButton(int floor) {
		floors[floor].clearUpButton();
	}

	void pressDownButton(int floor) {
		floors[floor].pressDownButton();
	}

	void clearDownButton(int floor) {
		floors[floor].clearDownButton();
	}

	void enterElevator(int floor, int elevator) {
		floors[floor].removeRider();
		elevators[elevator].addRider();
	}

	void pressFloorButton(int floor, int elevator) {
		elevators[elevator].pressFloorButton(floor);
	}

	void exitElevator(int floor, int elevator) {
		elevators[elevator].removeRider();
		floors[floor].addRider();
	}

	void elevatorMoved(int floor, int elevator) {
		elevators[elevator].elevatorMoved(floor);
	}

	private void paintRider(Graphics g, int x, int y, int r) {
		g.setColor(Color.yellow);

		g.fillOval(x - r, y - r, 2 * r, 2 * r);

		g.setColor(Color.black);

		g.fillOval(x - r / 2, y - r / 2, r / 3, r / 3);
		g.fillOval(x + r / 4, y - r / 2, r / 3, r / 3);

		g.drawArc(x - r / 2, y - r / 2, r, r, 210, 120);
	}

	private void paintRiders(Graphics g, int x, int y, int w, int h, int n) {
		int r = 8, t = 20;

		int xn = w / t;
		int yn = h / t;

		int x0 = x + (w - xn * t) / 2 + t / 2;
		int y0 = y + h - t / 2;

		for (int j = 0; j < yn; j++) {
			for (int i = 0; i < xn; i++) {
				if (n-- > 0)
					paintRider(g, x0 + i * t, y0 - j * t, r);
			}
		}
	}

	private class Floor extends Canvas {
		int floor, numRiders;

		boolean upSet = false;
		boolean downSet = false;

		Floor(int floor, int numRiders) {
			this.floor = floor;
			this.numRiders = numRiders;

			setBackground(Color.black);
		}

		public Dimension getPreferredSize() {
			return new Dimension(w, h);
		}

		public Dimension getMinimumSize() {
			return getPreferredSize();
		}

		public Dimension getMaximumSize() {
			return getPreferredSize();
		}

		public void repaint() {
			super.repaint();

			if (TCB.isNachosThread()) {
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
				}
			}
		}

		void pressUpButton() {
			if (!upSet) {
				upSet = true;
				repaint();
			}
		}

		void pressDownButton() {
			if (!downSet) {
				downSet = true;
				repaint();
			}
		}

		void clearUpButton() {
			if (upSet) {
				upSet = false;
				repaint();
			}
		}

		void clearDownButton() {
			if (downSet) {
				downSet = false;
				repaint();
			}
		}

		void addRider() {
			numRiders++;

			repaint();
		}

		void removeRider() {
			numRiders--;

			repaint();
		}

		public void paint(Graphics g) {
			g.setColor(Color.lightGray);
			g.drawLine(0, 0, w, 0);

			paintRiders(g, 0, 5, 3 * w / 4, h - 10, numRiders);

			paintButtons(g);
		}

		private void paintButtons(Graphics g) {
			int s = 3 * w / 4;

			int x1 = s + w / 32;
			int x2 = w - w / 32;
			int y1 = h / 8;
			int y2 = h - h / 8;

			g.setColor(Color.darkGray);
			g.drawRect(x1, y1, x2 - x1, y2 - y1);
			g.setColor(Color.lightGray);
			g.fillRect(x1 + 1, y1 + 1, x2 - x1 - 2, y2 - y1 - 2);

			int r = Math.min((x2 - x1) / 3, (y2 - y1) / 6);
			int xc = (x1 + x2) / 2;
			int yc1 = (y1 + y2) / 2 - (3 * r / 2);
			int yc2 = (y1 + y2) / 2 + (3 * r / 2);

			g.setColor(Color.red);

			if (floor < numFloors - 1) {
				if (upSet)
					g.fillOval(xc - r, yc1 - r, 2 * r, 2 * r);
				else
					g.drawOval(xc - r, yc1 - r, 2 * r, 2 * r);
			}

			if (floor > 0) {
				if (downSet)
					g.fillOval(xc - r, yc2 - r, 2 * r, 2 * r);
				else
					g.drawOval(xc - r, yc2 - r, 2 * r, 2 * r);
			}
		}
	}

	private class ElevatorShaft extends Canvas {
		ElevatorShaft(int elevator) {
			this.elevator = elevator;

			floorsSet = new boolean[numFloors];
			for (int i = 0; i < numFloors; i++)
				floorsSet[i] = false;

			setBackground(Color.black);
		}

		public Dimension getPreferredSize() {
			return new Dimension(w, h * numFloors);
		}

		public Dimension getMinimumSize() {
			return getPreferredSize();
		}

		public Dimension getMaximumSize() {
			return getPreferredSize();
		}

		private void repaintElevator() {
			repaint(s, h * (numFloors - 1 - Math.max(floor, prevFloor)), w - 2
					* s, h * 2);

			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
			}
		}

		void openDoors() {
			doorsOpen = true;

			repaintElevator();
		}

		void closeDoors() {
			doorsOpen = false;

			repaintElevator();
		}

		void setDirectionDisplay(int direction) {
			this.direction = direction;

			repaintElevator();
		}

		void pressFloorButton(int floor) {
			if (!floorsSet[floor]) {
				floorsSet[floor] = true;

				repaintElevator();
			}
		}

		void elevatorMoved(int floor) {
			prevFloor = this.floor;
			this.floor = floor;

			floorsSet[floor] = false;

			repaintElevator();
		}

		void addRider() {
			numRiders++;

			repaintElevator();
		}

		void removeRider() {
			numRiders--;

			repaintElevator();
		}

		public void paint(Graphics g) {
			g.setColor(Color.lightGray);

			if (g.hitClip(0, 0, s, h * numFloors)) {
				g.drawLine(0, 0, 0, h * numFloors);
				g.drawLine(s - 1, 0, s - 1, h * numFloors);
				for (int y = 0; y < h * numFloors - s; y += s)
					g.drawLine(0, y, s - 1, y + s - 1);
			}

			if (g.hitClip(w - s, 0, s, h * numFloors)) {
				g.drawLine(w - s, 0, w - s, h * numFloors);
				g.drawLine(w - 1, 0, w - 1, h * numFloors);
				for (int y = 0; y < h * numFloors - s; y += s)
					g.drawLine(w - s, y, w - 1, y + s - 1);
			}

			// rectangle containing direction display area
			Rectangle d = new Rectangle(s * 3 / 2, h * (numFloors - 1 - floor),
					w - 3 * s, w / 3 - s);

			// unit of measurement in direction rect (12ux4u)
			int u = d.width / 12;

			// draw elevator, fill riders
			Rectangle e = new Rectangle(d.x, d.y + d.height, d.width, h
					- d.height - u);
			g.drawRect(e.x, e.y, e.width, e.height);
			paintRiders(g, e.x, e.y, e.width, e.height, numRiders);

			g.setColor(Color.lightGray);

			// draw doors...
			if (doorsOpen) {
				g.drawLine(e.x + 2 * s, e.y, e.x + 2 * s, e.y + e.height);
				for (int y = 0; y < e.height - 2 * s; y += 2 * s)
					g.drawLine(e.x, e.y + y, e.x + 2 * s, e.y + y + 2 * s);

				g.drawLine(e.x + e.width - 2 * s, e.y, e.x + e.width - 2 * s,
						e.y + e.height);
				for (int y = 0; y < e.height - 2 * s; y += 2 * s)
					g.drawLine(e.x + e.width - 2 * s, e.y + y, e.x + e.width,
							e.y + y + 2 * s);
			} else {
				for (int x = 0; x < e.width; x += 2 * s)
					g.drawLine(e.x + x, e.y, e.x + x, e.y + e.height);
			}

			g.setColor(Color.yellow);

			int[] xUp = { d.x + u * 6, d.x + u * 8, d.x + u * 7 };
			int[] yUp = { d.y + u * 3, d.y + u * 3, d.y + u * 1 };

			int[] xDown = { d.x + u * 4, d.x + u * 6, d.x + u * 5 };
			int[] yDown = { d.y + u * 1, d.y + u * 1, d.y + u * 3 };

			// draw arrows
			if (direction == ElevatorBank.dirUp)
				g.fillPolygon(xUp, yUp, 3);
			else
				g.drawPolygon(xUp, yUp, 3);

			if (direction == ElevatorBank.dirDown)
				g.fillPolygon(xDown, yDown, 3);
			else
				g.drawPolygon(xDown, yDown, 3);
		}

		private static final int s = 5;

		private boolean doorsOpen = false;
		private int floor = 0, prevFloor = 0, numRiders = 0;
		private int direction = ElevatorBank.dirNeither;

		private int elevator;

		private boolean floorsSet[];
	}
}
